import os
import matplotlib.pyplot as plt
import pandas as pd
import numpy as np
import cv2
import matlab.engine
import scipy.io as scio
import math
import itertools

eng = matlab.engine.start_matlab()


def recover(img_data, ec, flags, bins, Dbis):
    row, col = img_data.shape
    secrect = np.zeros((1, row * col))[0]
    index = 0
    for i in range(1, row - 2):
        for j in range(1, col - 2):
            v1 = int(img_data[i - 1][j])
            v2 = int(img_data[i][j - 1])
            v3 = int(img_data[i + 1][j])
            v4 = int(img_data[i][j + 1])
            four_neighbor = np.array(
                [int(v1), int(v2), int(v3), int(v4)])
            p = int(np.ceil(np.average(four_neighbor)))
            em = int(img_data[i][j]) - int(p)
            if np.mod(i + j, 2) == 0 and flags == 0 and ec > 0:
                ks = Dbis[i][j] - 1
                if int(em) == bins[int(ks)] or int(em) == -bins[int(ks)] - 1:
                    ec = ec - 1
                    secrect[index] = 0
                    index += 1
                if int(em) == bins[int(ks)] + 1 or int(em) == -bins[int(ks)] - 2:
                    ec = ec - 1
                    secrect[index] = 1
                    index += 1
            if np.mod(i + j, 2) == 1 and flags == 1 and ec > 0:
                ks = Dbis[i][j] - 1
                if int(em) == bins[int(ks)] or int(em) == -bins[int(ks)] - 1:
                    secrect[index] = 0
                    ec - 1
                    index += 1
                if int(em) == bins[int(ks)] + 1 or int(em) == -bins[int(ks)] - 2:
                    secrect[index] = 1
                    ec - 1
                    index += 1
    return secrect

def plt_make(data, x_label, y_label, title):
    '''
    :param data: 生成直方图的数据
    :param x_label: x轴的标签
    :param y_label: y轴的标签
    :param title: 图片标题
    :return:
    '''
    # plt.rcParams['font.sans - serif'] = ['SimHei']
    print(data.shape)
    comment = pd.Series(data)
    print(comment.value_counts())
    comment.plot.hist(grid=True, bins=np.arange(-20, 20, 1),
                      color='#607c8e')
    plt.title(title)
    plt.xlim((-20, 20))
    plt.xlabel(x_label)
    plt.ylabel(y_label)
    plt.grid(axis='y', alpha=0.75)
    # plt.legend(prop={'size': 10})
    plt.show()


def Over_flow(img, flag):
    '''
    防止溢出
    :param img:
    :return:
    '''
    row, col = img.shape
    lcm = np.zeros((1, col * row))
    index = 0
    for i in range(1, row - 2):
        for j in range(1, col - 2):
            if np.mod(i + j, 2) == 0 and flag == 0:
                index += 1
                if img[i][j] == 0:
                    img[i][j] == 1
                    lcm[index] = 1
                if img[i][j] == 255:
                    img[i][j] = 254
                    lcm[index] = 1
            if np.mod(i + j, 2) == 1 and flag == 1:
                index += 1
                if img[i][j] == 0:
                    img[i][j] == 1
                    lcm[index] = 1
                if img[i][j] == 255:
                    img[i][j] = 254
                    lcm[index] = 1

    xc = np.empty((1, 1), dtype=object)

    for i in range(1):
        for k in range(1):
            xc[i, k] = np.zeros((1, col * row))
    print('xc', xc.shape)
    # # xC = {}
    xc[0][0] = lcm
    print(xc)
    file_name = 'data.mat'
    scio.savemat(file_name, {'xc': lcm})
    # data = eng.test()
    data = []
    return img, lcm, index, data


def searchbins(H, ECR):
    bins = np.zeros(16);
    # R = C / D;
    # EC = 0;
    # L = len(W);
    Dmin = 10000000000000000
    # u7 = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 250]
    u7 = [0, 1, 2, 3, 4, 5, 6, 7, 250]
    L = len(u7)
    for a1 in range(0, L):
        for a2 in range(a1, L):
            for a3 in range(a2, L):
                for a4 in range(a3, L):
                    for a5 in range(a4, L):
                        for a6 in range(a5, L):
                            for a7 in range(a6, L):
                                for a8 in range(a7, L):
                                    for a9 in range(a8, L):
                                        for a10 in range(a9, L):
                                            for a11 in range(a10, L):
                                                for a12 in range(a11, L):
                                                    for a13 in range(a12, L):
                                                        for a14 in range(a13, L):
                                                            for a15 in range(a14, L):
                                                                for a16 in range(a15, L):
                                                                    S = [u7[a1], u7[a2], u7[a3], u7[a4], u7[a5],
                                                                         u7[a6], u7[a7], u7[a8], u7[a9], u7[a10],
                                                                         u7[a11], u7[a12], u7[a13], u7[a14],
                                                                         u7[a15], u7[a16]]

                                                                    x = 0
                                                                    for k in range(16):
                                                                        x = x + H[S[k] + 254, k] + H[-S[k] - 1 + 254, k]
                                                                    if x >= ECR:
                                                                        y = 0
                                                                        for k in range(16):
                                                                            y = y + np.sum(
                                                                                H[S[k] + 254:512,
                                                                                k]) + np.sum(
                                                                                H[0:-S[k] - 1 + 255, k])
                                                                            # y = y + np.sum(
                                                                            #     H[S[k] + 254:512, k]) + np.sum(
                                                                            #     H[0:-S[k] - 1 + 254, k])
                                                                        if y / x < Dmin:
                                                                            if x == 5222.0:
                                                                                print('此时的s', S)
                                                                            # print(y,x,[a1,a2,a3,a4,a5,a6,a7,a8,a9,a10,a11,a12,a13,a14,a15,a16])
                                                                            Dmin = y / x
                                                                            bins = S
    print('this is bins', bins)
    # print('this is test', test)
    print("s的直", S)
    return bins


class Load_img():
    '''
        初始化加载图像，通过getitem方法可以直接将所有的图片通过cv2将图片加载
    '''

    def __init__(self, root_dir, flag):
        '''
        :param root_dir: 选择图形所在目录
        '''
        self.root_dir = root_dir
        self.path = os.path.join(self.root_dir)
        self.img_path = os.listdir(self.path)
        # self.flag = flag
        # self.lcm = []
        # # print(self.img_path)
        # self.halfsize = 0
        # self.data = []
        # self.data = np.array([])
        # self.label = np.array([])

    def __getitem__(self, item):
        img_name = self.img_path[item]
        img_item_path = os.path.join(self.root_dir, img_name)
        print(img_item_path)
        # 直接获取灰度图
        img = cv2.imread(img_item_path, 0)
        # img, lcm, index,data = Over_flow(img, self.flag)
        # self.lcm = lcm
        # self.halfsize = index
        # self.data = data
        return img


def img_division(img, flag):
    '''
        读取图形的数据，计算图像的复杂度给图像分类
        :param img:图像
        :param flag:判断是奇数点还是偶数点
        return 返回lc为复杂度矩阵，erromap为误差矩阵，predict为预测像素矩阵

    '''
    row, col = img.shape
    # 复杂度
    Complexity = np.zeros((row, col))
    # 预测点
    predict = np.zeros((row, row))
    # 误差值
    erromap = np.zeros((row, row))
    # row,col获取图像的行和列lc先定义一个和图像尺寸相同的0矩阵data ，label 为空列表
    index = 0
    for r in range(1, row - 2):
        for c in range(1, col - 2):
            if np.mod(r + c, 2) == 0 and flag == 0:
                v1 = int(img[r - 1][c])
                v2 = int(img[r][c - 1])
                v3 = int(img[r + 1][c])
                v4 = int(img[r][c + 1])  # 像素上下左右四个像素
                x = int(img[r][c])
                u6 = int(img[r - 1][c + 2])
                u7 = int(img[r][c + 2])
                u1 = int(img[r + 1][c - 1])
                u4 = int(img[r + 1][c + 1])
                u8 = int(img[r + 1][c + 2])
                u2 = int(img[r + 2][c - 1])
                u3 = int(img[r + 2][c])
                u5 = int(img[r + 2][c + 1])
                u9 = int(img[r + 2][c + 2])
                # u6到u9为像素周围的九个圈的像素
                f0 = abs(v2 - u1)
                f1 = abs(u1 - u2)
                f2 = abs(v3 - u3)
                f3 = abs(v4 - u4)
                f4 = abs(u4 - u5)
                f5 = abs(u6 - u7)
                f6 = abs(u7 - u8)
                f7 = abs(u8 - u9)
                f8 = abs(v4 - u7)
                fL = abs(u1 - v3)
                f_0 = abs(v3 - u4)
                f_1 = abs(u4 - u8)
                f_2 = abs(u2 - u3)
                f_3 = abs(u3 - u5)
                f_4 = abs(u5 - u9)

                four_neighbor = np.array(
                    [int(v1), int(v2), int(v3), int(v4)])
                p = int(np.ceil(np.average(four_neighbor)))
                D = abs(v2 - u1) + abs(u1 - u2) + abs(v1 - p) + abs(p - v3) + abs(v3 - u3) + abs(v4 - u4) + abs(
                    u4 - u5) + abs(u6 - u7) + abs(u7 - u8) + abs(u8 - u9) + abs(v2 - p) + abs(p - v4) + abs(
                    v4 - u7) + abs(u1 - v3) + abs(v3 - u4) + abs(u4 - u8) + abs(u2 - u3) + abs(u3 - u5) + abs(
                    u5 - u9)
                Complexity[r][c] = D

                predict[r][c] = int(p)
                erromap[r][c] = int(img[r][c]) - int(p)
                if erromap[r][c] == 0:
                    index += 1
            if np.mod(r + c, 2) == 1 and flag == 1:
                v1 = int(img[r - 1][c])
                v2 = int(img[r][c - 1])
                v3 = int(img[r + 1][c])
                v4 = int(img[r][c + 1])  # 像素上下左右四个像素
                x = int(img[r][c])
                u6 = int(img[r - 1][c + 2])
                u7 = int(img[r][c + 2])
                u1 = int(img[r + 1][c - 1])
                u4 = int(img[r + 1][c + 1])
                u8 = int(img[r + 1][c + 2])
                u2 = int(img[r + 2][c - 1])
                u3 = int(img[r + 2][c])
                u5 = int(img[r + 2][c + 1])
                u9 = int(img[r + 2][c + 2])
                # u6到u9为像素周围的九个圈的像素
                f0 = abs(v2 - u1)
                f1 = abs(u1 - u2)
                f2 = abs(v3 - u3)
                f3 = abs(v4 - u4)
                f4 = abs(u4 - u5)
                f5 = abs(u6 - u7)
                f6 = abs(u7 - u8)
                f7 = abs(u8 - u9)
                f8 = abs(v4 - u7)
                fL = abs(u1 - v3)
                f_0 = abs(v3 - u4)
                f_1 = abs(u4 - u8)
                f_2 = abs(u2 - u3)
                f_3 = abs(u3 - u5)
                f_4 = abs(u5 - u9)
                four_neighbor = np.array(
                    [int(v1), int(v2), int(v3), int(v4)])
                p = int(np.ceil(np.average(four_neighbor)))
                D = abs(v2 - u1) + abs(u1 - u2) + abs(v1 - p) + abs(p - v3) + abs(v3 - u3) + abs(v4 - u4) + abs(
                    u4 - u5) + abs(u6 - u7) + abs(u7 - u8) + abs(u8 - u9) + abs(v2 - p) + abs(p - v4) + abs(
                    v4 - u7) + abs(u1 - v3) + abs(v3 - u4) + abs(u4 - u8) + abs(u2 - u3) + abs(u3 - u5) + abs(
                    u5 - u9)
                Complexity[r][c] = D
                predict[r][c] = int(p)
                erromap[r][c] = int(img[r][c]) - int(p)
                if erromap[r][c] == 0:
                    index += 1
    print('em_index', index)
    return Complexity, erromap, predict


def divide_hs(Complexity, erromap, flag):
    '''
        2d直方图
    '''
    row, col = Complexity.shape
    # 直方图序列
    print('compu', np.max(Complexity))
    hs = np.zeros((512, int(np.max(Complexity) + 1)))
    print(hs.shape)
    for i in range(1, row - 2):
        for j in range(1, col - 2):
            if np.mod(i + j, 2) == 0 and flag == 0:
                # if int(Complexity[i][j]) == 6 and erromap[i][j] == -1:
                #     print('254', i, j)
                hs[int(erromap[i][j] + 255)][int(Complexity[i][j])] += 1
            if np.mod(i + j, 2) == 1 and flag == 1:
                hs[int(erromap[i][j] + 255)][int(Complexity[i][j])] += 1
    return hs


def CD(hist):
    W = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 250]
    C = np.zeros((16, 16));
    D = np.zeros((16, 16))
    for i in range(16):
        for j in range(16):
            C[i][j] = hist[255 + W[j]][i] + hist[255 - W[j] - 1][i]
            y1 = 0;
            y2 = 0
            for k in range(W[j] + 255, 511):
                y1 = y1 + hist[k][i]
            for n in range(0, -W[j] - 1 + 255):
                y2 = y2 + hist[n][i]
            D[i][j] = y1 + y2
    # R=C/D
    # print(R)
    return C, D


# def cal_aux(T,):

# ----------------------------------------李小龙版本start
def guiyi(k, Complexity, hs, size):
    '''
        k:划分的区间
        complexity:复杂度矩阵
        hs:2d直方图,
        进行区间划分，划分16个区间，每个区间保证对应有相等数量的坐标点
    '''
    T = np.zeros(k)
    for i in range(k):
        for j in range(int(np.max(Complexity)) + 2):
            # print(np.sum(np.sum(hs[:, 1:j])))
            if np.sum(np.sum(hs[:, 0:j])) >= ((size / k) * (i + 1)):
                T[i] = j
                break
    return T


def recomplex(row, col, k, T, flag, complex):
    '''
        row,col长宽
        k分割线区间
        T归一化后断点
        flag 奇偶
        complex原先的复杂度
    '''
    # 新的复杂度矩阵
    Dbis = np.zeros((row, col))
    for i in range(k - 1, -1, -1):
        for j in range(1, row - 2):
            for m in range(1, col - 2):
                if np.mod(m + j, 2) == 0 and flag == 0:
                    if complex[j][m] < T[i]:
                        Dbis[j][m] = i + 1

                if np.mod(m + j, 2) == 1 and flag == 1:
                    if complex[j][m] < T[i]:
                        Dbis[j][m] = i + 1
    return Dbis


def re_hs(k, row, col, Dbis, flag, em):
    Hs = np.zeros((511, k))
    for i in range(1, row - 1):
        for j in range(1, col - 1):
            if np.mod(i + j, 2) == 0 and flag == 0:
                Hs[int(em[i][j] + 254)][int(Dbis[i][j]) - 1] += 1
            if np.mod(i + j, 2) == 1 and flag == 1:
                Hs[int(em[i][j] + 254)][int(Dbis[i][j]) - 1] += 1
    return Hs


def cal_aux1(s, k, lcm, T, data_length):
    '''
        k 是分割数量
        lcm是localtionmapc
        t是分割区间
    '''
    print('s,k', [s, k])
    if np.sum(lcm) == 0:

        Aux = k * 3 + 4 + 1 + 17 + (k - 1) * s + 4
    else:
        Aux = k * 3 + 4 + 1 + 8 * data_length + 17 + 17 + (k - 1) * s + 4
    return Aux


def cal_aux2(s, SIB, lcm, T, data_length):
    '''
        k 是分割数量
        lcm是localtionmapc
        t是分割区间
    '''
    print('s2,scale', [s, SIB])
    if np.sum(lcm) == 0:
        Aux = SIB * 3 + 4 + 1 + 17 + SIB * s + 4
    else:
        Aux = SIB * 3 + 4 + 1 + 8 * data_length + 17 + 17 + SIB * s + 4
    return Aux


# -------------------------------李小龙版本end

def cal_psnr(img, img_hide, ec):
    A, B = img.shape
    mse = 0
    PSNR = 0
    mse = int(np.sum(np.sum(np.square(img - img_hide))))
    print('mse',mse)
    if ec == 0:
        PSNR = 10 * np.log10((A * B * 255 * 255) / mse)
    return PSNR

def cal_aux(checklist,flag,data_length,scale,R,lcm):
    cl = 0
    test =[ ]
    for i in range(16):
        test.append(i)
    checklist = np.array(checklist) + 1
    # checklist = np.array(test)+1
    print('incheck',checklist)
    if flag == 0:
        for i in range(16):
            if checklist[i] == i:
                cl = cl
            else:
                print(i)
                cl = cl + np.ceil(np.log2(checklist[i]))
            if np.sum(lcm) == 0:
                aux = scale * 3 + 4 + 1 + 17 + cl + 4
            else:
                aux = scale * 3 + 4 + 1 + data_length*8 + 17 + 17 + cl + 4
    elif flag == 1:  # 根据寻优的bins更新辅助信息的长度
        for i in range(16):
            if R[i] < 250 and checklist[i] > i:
                cl = cl + np.ceil(math.log(checklist[i], 2))
            if R[i] == 250:
                scale = scale - 1
        if np.sum(lcm) == 0:
            aux = scale * 3 + 4 + 1 + 17 + cl + 4
        else:
            aux = scale * 3 + 4 + 1 + data_length*8 + 17 + 17 + cl + 4
    print('The aux is ', flag, ' ', aux)
    return aux


def cal_entropy(hs,k):
    '''

    :param hs: 新生成的直方图
    :param flag: 判断奇偶
    :return:
    '''
    cal_ec = np.array([])
    checklist =np.array([])
    print(hs.shape)
    print(k)
    for i in range(k):
        c =np.sum(hs[:,i])
        total = len(hs[:,i])
        pro = int(c)/total
        print('c,total',[c,total])
        e = pro * np.log2(1/pro)
        cal_ec=np.append(cal_ec,e)
    cal_test = np.sort(cal_ec)
    print(cal_test)
    print(cal_ec)
    for i in range(k):
        for j in range(k):
            print([i,j])
            if cal_ec[i] == cal_test[j]:
                checklist=np.append(checklist,j)
                cal_test[j] = -1
    return checklist